package com.my.ph.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.my.ph.config.entity.UserJsonConfig;
import com.my.ph.dto.PublishHelperParam;
import com.my.ph.entity.media.MediaInfo;
import com.my.ph.entity.media.TVMovieDetail;
import com.my.ph.entity.publish.PublishFileInfo;
import com.my.ph.entity.publish.PublishHelperInfo;
import com.my.ph.enums.MediaTypeEnum;
import com.my.ph.service.PublishHelperInfoService;
import com.my.ph.service.UploadService;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
@Slf4j
public class TvMovieUtil {

    // 打包命令 D:\Envs\Java\jdk-17.0.3+7\bin\jpackage --type exe --input D:\Project\Java\publish_helper\target --main-jar ph-0.0.1-SNAPSHOT.jar --name publish_helper  --win-dir-chooser --verbose --win-console
    @Resource(name = "seedvault")
    private UploadService uploadService;
    @Autowired
    private PublishHelperInfoService publishHelperInfoService;

    private Integer extractNumber(String input, String chineseFlag, String placeholder, String placeholderKey, String... englishFlags) {
        if (StrUtil.isNotBlank(placeholder)) {
            String regex = placeholder.replace("[", "\\[").replace("]", "\\]").replace("!", "\\!")
                    .replace("(", "\\(").replace(")", "\\)").replace(".", "\\.");
            Pattern placeholderPattern = Pattern.compile(regex.replace(placeholderKey, "(\\d+)").replaceAll("\\{.*}", ".*"));
            Matcher placeholderMatcher = placeholderPattern.matcher(input);
            if (placeholderMatcher.find()) {
                String numberStr = placeholderMatcher.group(1);
                return Integer.parseInt(numberStr);
            }
        }

        // 先尝试匹配中文数字
        Pattern chinesePattern = Pattern.compile("第([十零一二三四五六七八九十百千万]+)" + chineseFlag);
        Matcher chineseMatcher = chinesePattern.matcher(input);
        if (chineseMatcher.find()) {
            String numberStr = chineseMatcher.group(1);
            return ConverterNumberUtil.chineseToNumber(numberStr);
        }

        // 再尝试匹配英文缩写格式
        for (String flag : englishFlags) {
            Pattern pattern = Pattern.compile(flag + "\\s*(\\d+)");
            Matcher matcher = pattern.matcher(input);
            if (matcher.find()) {
                return Integer.parseInt(matcher.group(1));
            }
        }
        return null;
    }

    public Integer getSeasonNum(String input, String placeholder) {
        return extractNumber(input, "季", placeholder, "{se}", "S", "SE", "season", "Season");
    }

    public Integer getEpisodeNum(String input, String placeholder) {
        return extractNumber(input, "集", placeholder, "{ep}", "E", "EP", "episode", "Episode");
    }

    public String getIntroTemplate(TVMovieDetail movieDetail, List<String> imageList, String mediaType) {
        String introTemplate = ResourceUtil.readUtf8Str("templates/intro.txt");
        Map<String, Object> values = new HashMap<>();
        values.put("posterPath", movieDetail.getPosterPath());
        values.put("title", movieDetail.getTitle());
        values.put("titles", StrUtil.join("/", movieDetail.getTitles().stream().map(TVMovieDetail.AlternativeTitles::getTitle).toList()));
        values.put("year", movieDetail.getYear());
        values.put("originZhCountryName", movieDetail.getOriginZhCountryName());
        values.put("genres", StrUtil.join("/", movieDetail.getGenres().stream().map(TVMovieDetail.Genre::getName).toList()));
        values.put("spokenLanguages", StrUtil.join("/", movieDetail.getSpokenLanguages().stream().map(TVMovieDetail.SpokenLanguage::getName).toList()));
        values.put("releaseDate", StrUtil.equals(mediaType, MediaTypeEnum.TV.getValue()) ? movieDetail.getFirstAirDate() : movieDetail.getReleaseDate());
        values.put("runtime", movieDetail.getRuntime());
        values.put("imdbUrl", movieDetail.getImdbId() != null ? "https://www.imdb.com/title/" + movieDetail.getImdbId() : null);
        values.put("episodesNumber", movieDetail.getNumberOfEpisodes());
        values.put("directingCredits", Optional.ofNullable(movieDetail.getDirectingCredits()).map(list -> StrUtil.join("/", list.stream().map(item -> item.getName() + " " + (StrUtil.equals(item.getName(), item.getOriginalName()) ? "" : item.getOriginalName())).toList())).orElse(null));
        values.put("writingCredits", Optional.ofNullable(movieDetail.getWritingCredits()).map(list -> StrUtil.join("/", list.stream().map(item -> item.getName() + " " + (StrUtil.equals(item.getName(), item.getOriginalName()) ? "" : item.getOriginalName())).toList())).orElse(null));
        values.put("actingCredits", Optional.ofNullable(movieDetail.getActingCredits()).map(list -> StrUtil.join("\n　　　　  　", list.stream().map(item -> item.getName() + " " + (StrUtil.equals(item.getName(), item.getOriginalName()) ? "" : item.getOriginalName())).toList())).orElse(null));
        values.put("overview", movieDetail.getOverview());
        values.put("image", StrUtil.join("\n", imageList.stream().map(item -> StrUtil.format("[img]{}[/img]", item)).toList()));
        return TemplateReplaceUtil.templateReplaceNullDelete(introTemplate, values);
    }


    public List<File> getPublishFile(File inputFile) {
        List<File> files = new ArrayList<>();
        if (inputFile.isFile()) files.add(inputFile);
        else files = FileUtil.loopFiles(inputFile);
        if (CollectionUtil.isNotEmpty(files)) files = files.stream().filter(f -> TypeFileUtil.isVideoFile(f.getName()) ||
                UserJsonConfigUtil.getInstance().getVideoFormatList().contains(Optional.ofNullable(FileUtil.getSuffix(f)).map(String::toUpperCase).orElse(""))).toList();
        if (CollectionUtil.isEmpty(files)) throw new RuntimeException("找不到视频文件！");
        return files;
    }

    public Map<String, Object> getPublishSubTitle(TVMovieDetail detail, MediaInfo info) {
        Map<String, Object> result = new HashMap<>();
        List<String> titles = new ArrayList<>(detail.getTitles().stream().map(TVMovieDetail.AlternativeTitles::getTitle).toList());
        if (!titles.contains(detail.getTitle())) titles.add(0, detail.getTitle());
        result.put("titles", StrUtil.join(" / ", titles));
        result.put("genresName", StrUtil.join(" / ", detail.getGenres().stream().map(TVMovieDetail.Genre::getName).toList()));
        result.put("actingName", StrUtil.join(" / ", detail.getActingCredits().stream().map(TVMovieDetail.Credits::getName).toList()));
        if (MediaTypeEnum.TV.is(detail.getMediaType())) {
            Map<Integer, TVMovieDetail.Season> seasonMap = detail.getSeasons().stream().collect(Collectors.toMap(TVMovieDetail.Season::getSeasonNumber, Function.identity(), (k1, k2) -> k1));
            result.put("seasonName", Optional.ofNullable(seasonMap.get(info.getSeason())).map(TVMovieDetail.Season::getName).orElse(""));
            result.put("episodeName", Optional.ofNullable(info.getEpisode()).map(ep -> "第" + ep + "集").orElse(""));
        }
        return result;
    }

    private MediaInfo getMediaInfo(TVMovieDetail movieDetail, PublishHelperParam param, File file) {
        return MediaInfo.builder().originalTitle(movieDetail.getOriginalTitle()).enTitle(movieDetail.getEnTitle())
                .year(movieDetail.getYear()).source(param.getSource()).team(param.getTeam())
                .season(param.getSeason() == null ? getSeasonNum(file.getName(), param.getSePlaceholder()) : param.getSeason())
                .episode(param.getEpisode() == null ? getEpisodeNum(file.getName(), param.getEpPlaceholder()) : param.getEpisode())
                .videoMediaInfo(file).build();
    }

    private List<String> setImageList(Duration duration, File file) {
        List<String> imageList = new ArrayList<>();
        imageList.add(uploadService.upload(GenerateImageUtil.generateImage(file.getPath(), 100)));
        imageList.add(uploadService.upload(GenerateImageUtil.generateImage(file.getPath(), 150)));
        List<String> pics = new ArrayList<>();
        for (int i = 0; i < 9; i++) pics.add(GenerateImageUtil.generateImage(file.getPath(), RandomUtil.randomInt(200, (int) duration.toSeconds())));
        imageList.add(uploadService.upload(ImagePalaceUtil.palace(pics, null)));
        return imageList;
    }

    public PublishHelperInfo getPublishMovie(TVMovieDetail movieDetail, PublishHelperParam param, File inputFile) {
        UserJsonConfig jsonConfig = UserJsonConfigUtil.getInstance();
        List<MediaInfo> mediaInfos = getPublishFile(inputFile).stream().map(file -> getMediaInfo(movieDetail, param, file)).toList();
        if (CollectionUtil.isEmpty(mediaInfos)) throw new RuntimeException("找不到媒体文件信息！");
        List<String> imageList = mediaInfos.stream().filter(mediaInfo -> mediaInfo.getDuration().toMinutes() >= 10).findFirst().
                map(mediaInfo -> setImageList(mediaInfo.getDuration(), mediaInfo.getFile())).orElse(ListUtil.empty());
        MediaInfo mediaInfoZero = mediaInfos.get(0);
        String folderName = TemplateReplaceUtil.templateReplace(jsonConfig.getFileNameMovie(), BeanUtil.beanToMap(mediaInfoZero)).replaceAll("\\.+", ".");
        String folderPath = Paths.get(inputFile.getParent(), folderName).toString();
        String torrentPath;
        return PublishHelperInfo.builder()
                .filePath(folderPath).mediaType(movieDetail.getMediaType())
                .mainTitle(TemplateReplaceUtil.templateReplace(jsonConfig.getMainTitleMovie(), BeanUtil.beanToMap(mediaInfoZero)))
                .subTitle(TemplateReplaceUtil.templateReplace(jsonConfig.getSubTitleMovie(), getPublishSubTitle(movieDetail, mediaInfoZero)))
                .folderName(folderName).inform(mediaInfoZero.getInform())
                .intro(getIntroTemplate(movieDetail, imageList == null ? ListUtil.empty() : imageList, param.getMediaType()))
                .inform(mediaInfoZero.getInform())
                .mainTitle(TemplateReplaceUtil.templateReplace(jsonConfig.getMainTitleMovie(), BeanUtil.beanToMap(mediaInfoZero)))
                .subTitle(TemplateReplaceUtil.templateReplace(jsonConfig.getSubTitleMovie(), getPublishSubTitle(movieDetail, mediaInfoZero)))
                .source(param.getSource())
                .host(param.getHost())
                .publishFileList(mediaInfos.stream().map(mediaInfo -> {
                    String fileName = TemplateReplaceUtil.templateReplace(jsonConfig.getFileNameMovie(), BeanUtil.beanToMap(mediaInfo)).replaceAll("\\.+", ".");
                    PublishFileInfo fileInfo = PublishFileInfo.builder()
                            .fileName(fileName)
                            .filePath(folderPath + File.separator + fileName + "." + mediaInfo.getSuffix())
                            .inform(mediaInfo.getInform())
                            .inputFilePath(mediaInfo.getFile().getPath())
                            .mediaInfo(mediaInfo).build();
//                    FileUtil.move(mediaInfo.getFile(), FileUtil.file(fileInfo.getFilePath()), true);
                    return fileInfo;
                }).toList())
//                .torrentPath(torrentPath = TorrentUtil.createTorrent(folderPath, folderName, param.getSource()))
//                .torrentUrl(StrUtil.format(StrUtil.format("{}/api/fileDirectory/getTorrent?torrentPath={}", param.getHost(), URLUtil.encode(torrentPath))))
                .mediaInfo(mediaInfoZero).build();
    }

    public PublishHelperInfo getPublishTv(TVMovieDetail movieDetail, PublishHelperParam param, File inputFile) {
        UserJsonConfig jsonConfig = UserJsonConfigUtil.getInstance();
        Set<Integer> seasonSet = new TreeSet<>();
        AtomicReference<String> lastErrorMsg = new AtomicReference<>(null);
        List<MediaInfo> mediaInfos = getPublishFile(inputFile).stream().map(file -> {
            MediaInfo mediaInfo = getMediaInfo(movieDetail, param, file);
            lastErrorMsg.set(ObjUtil.isNull(mediaInfo.getSeason()) ? "未识别到季信息" : ObjUtil.isNull(mediaInfo.getEpisode()) ? "未识别到集信息" : null);
            if (lastErrorMsg.get() != null) return null;
            seasonSet.add(mediaInfo.getSeason());
            return mediaInfo;
        }).filter(Objects::nonNull).toList();
        if (CollectionUtil.isEmpty(mediaInfos)) throw new RuntimeException(lastErrorMsg.get() == null ? "找不到媒体文件信息！" : lastErrorMsg.get());
        List<String> imageList = mediaInfos.stream().filter(mediaInfo -> mediaInfo.getDuration().toMinutes() >= 10).findFirst().
            map(mediaInfo -> setImageList(mediaInfo.getDuration(), mediaInfo.getFile())).orElse(ListUtil.empty());
        MediaInfo mediaInfoZero = mediaInfos.get(0);
        String folderName = TemplateReplaceUtil.templateReplace(mediaInfos.size() == 1 ? jsonConfig.getFolderNameTvSeasonEpisode() : seasonSet.size() == 1 ?
                        jsonConfig.getFolderNameTvSeason() : jsonConfig.getFolderNameTv(), BeanUtil.beanToMap(mediaInfoZero)).replaceAll("\\.+", ".").replaceAll(":", "：");
        String folderPath = Paths.get(inputFile.getParent(), folderName).toString();
        String torrentPath;
        return PublishHelperInfo.builder()
                .filePath(folderPath).mediaType(movieDetail.getMediaType())
                .mainTitle(TemplateReplaceUtil.templateReplace(mediaInfos.size() == 1 ? jsonConfig.getMainTitleTvSeasonEpisode() : seasonSet.size() == 1 ?
                        jsonConfig.getMainTitleTvSeason() : jsonConfig.getMainTitleTv(), BeanUtil.beanToMap(mediaInfoZero)))
                .subTitle(TemplateReplaceUtil.templateReplace(mediaInfos.size() == 1 ? jsonConfig.getSubTitleTvSeasonEpisode() : seasonSet.size() == 1 ?
                        jsonConfig.getSubTitleTvSeason() : jsonConfig.getSubTitleTv(), getPublishSubTitle(movieDetail, mediaInfoZero)))
                .folderName(folderName).inform(mediaInfoZero.getInform())
                .inform(mediaInfoZero.getInform())
                .intro(getIntroTemplate(movieDetail, imageList == null ? ListUtil.empty() : imageList, param.getMediaType()))
                .seasons(seasonSet.stream().toList())
                .source(param.getSource())
                .host(param.getHost())
                .publishFileList(mediaInfos.stream().map(mediaInfo -> {
                    String fileName = TemplateReplaceUtil.templateReplace(jsonConfig.getFileNameTv(), BeanUtil.beanToMap(mediaInfo));
                    PublishFileInfo fileInfo = PublishFileInfo.builder()
                            .fileName(fileName)
                            .filePath(folderPath + (jsonConfig.isAutoCreateSeasonFolder() && seasonSet.size() > 1 ? File.separator + "Season " + mediaInfo.getSeason() : "")
                                    + File.separator + fileName + "." + mediaInfo.getSuffix())
                            .season(mediaInfo.getSeason())
                            .inform(mediaInfo.getInform())
                            .inputFilePath(mediaInfo.getFile().getPath())
                            .mediaInfo(mediaInfo).build();
//                    FileUtil.move(mediaInfo.getFile(), FileUtil.file(fileInfo.getFilePath()), true);
                    return fileInfo;
                }).toList()).mediaInfo(mediaInfoZero)
//                .torrentPath(torrentPath = TorrentUtil.createTorrent(folderPath, folderName, param.getSource()))
//                .torrentUrl(StrUtil.format(StrUtil.format("{}/api/fileDirectory/getTorrent?torrentPath={}", param.getHost(), URLUtil.encode(torrentPath))))
                .build();
    }

}
